﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Resources;
using System.Text.RegularExpressions;
using System.Web;

namespace blackhouse.Translator.Web {
    internal class FalbackResourceClass {

        #region Fields

        private static readonly Regex FindCultureRegExp = new Regex(@"\.([a-z]{2}(-[a-z]{2})?)\.resx$", RegexOptions.Compiled | RegexOptions.IgnoreCase);
        private readonly List<string> resxFiles;
        private readonly Dictionary<CultureInfo, Dictionary<string, string>> resources;

        #endregion

        #region Constructors

        internal FalbackResourceClass(string className) {
            HttpServerUtility server = HttpContext.Current.Server;
            this.resxFiles = new List<string>(
                Directory.GetFiles(server.MapPath("~/App_GlobalResources"), className + ".*.resx")
            );
            var fi = new FileInfo(server.MapPath("~/App_GlobalResources/" + className + ".resx"));
            if (fi.Exists)
                this.resxFiles.Add(fi.FullName);
            this.resources = this.Instantiate();
        }

        #endregion

        #region Methods

        private Dictionary<CultureInfo, Dictionary<string, string>> Instantiate() {
            var allResources = new Dictionary<CultureInfo, Dictionary<string, string>>();
            foreach (string filePath in this.resxFiles.OrderByDescending(f => f)) { // sortowanie powoduje, że w sytuacji gdzie są dwa pliki: ClassName.pl.resx oraz 
                var fi = new FileInfo(filePath);
                if (!fi.Exists) continue;
                var currentCulture = this.GetCultureInfoFromFileName(fi.Name);
                Dictionary<string, string> readResource;
                using (FileStream fs = fi.OpenRead()) {
                    readResource = this.ReadResource(fs);
                }
                if (allResources.ContainsKey(currentCulture))
                    allResources.Remove(currentCulture);
                allResources.Add(currentCulture, readResource);
            }
            return allResources;
        }

        private CultureInfo GetCultureInfoFromFileName(string file) {
            Match m = FindCultureRegExp.Match(file);
            return m.Success ? new CultureInfo(m.Groups[1].Value) : CultureInfo.InvariantCulture;
        }

        private Dictionary<string, string> ReadResource(FileStream openResourceFile) {
            var readResource = new Dictionary<string, string>();
            using (var reader = new ResXResourceReader(openResourceFile)) {
                foreach (DictionaryEntry entry in reader) {
                    readResource.Add(entry.Key.ToString(), entry.Value.ToString());
                }
            }
            return readResource;
        }

        internal IList<string> GetFilesPath() {
            return this.resxFiles;
        }

        internal string GetValue(string resourceKey, CultureInfo culture, CultureInfo fallbackCulture) {
            var collQ = new Queue<CultureInfo>(5);
            if (culture != null) {
                collQ.Enqueue(culture);
                if (!culture.IsNeutralCulture)
                    collQ.Enqueue(culture.Parent);
            }
            if (fallbackCulture != null) {
                collQ.Enqueue(fallbackCulture);
                if (!fallbackCulture.IsNeutralCulture)
                    collQ.Enqueue(fallbackCulture.Parent);
            }
            collQ.Enqueue(CultureInfo.InvariantCulture);
            return this.GetFirstValue(resourceKey, collQ);
        }

        private string GetFirstValue(string resourceKey, Queue<CultureInfo> cis) {
            while (cis.Count > 0) {
                var ci = cis.Dequeue();
                if (!this.resources.ContainsKey(ci)) continue;
                if (!this.resources[ci].ContainsKey(resourceKey)) continue;
                var value = this.resources[ci][resourceKey];
                if (!String.IsNullOrEmpty(value))
                    return this.resources[ci][resourceKey];
            }
            return String.Empty;
        }

        #endregion

    }
}